package cz.semjobKB.conllx.api.impl;

import cz.semjobKB.conllx.api.IProcessTreexOutput;
import cz.semjobKB.conllx.node.EConstituent;
import cz.semjobKB.conllx.node.EWordType;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

/**
 * The Class ExtractInfo.
 */
@Service
public class ProcessTreexOutput implements IProcessTreexOutput {

    private static final int NEGATION_POSITION_TAG = 10;
    // get the word class
    final private int WORD_CLASS_POSITION = 0;
    final private int GENUM_INDEX = 2;
    final private int NUMBER_INDEX = 3;
    final private int FALL_INDEX = 4;
    final private int PERSON_INDEX = 7;

    /**
     * Extracts the information from the treex results and constructs the
     * document tree.
     *
     * @param treexOutput  The processed document from the tool_chain
     * @param originalText The original text
     * @return the document tree
     * @throws Exception general exception
     */
    public Document parseDocument(final String treexOutput, final String originalText) throws Exception {
        String[] documentParsedInLines = treexOutput.split("\n");
        Document documentTree = new Document(originalText);
        Paragraph currentParagraph = null;
        Sentence currentSentence = null;
        Word currentWord = null;

        Sentence previousSentence = null;
        Word previousWord = null;

        currentParagraph = new Paragraph(documentTree);
        documentTree.getParagraphs().add(currentParagraph);
        currentSentence = new Sentence(documentTree, currentParagraph);
        previousSentence = currentSentence;
        currentParagraph.getSentences().add(currentSentence);
        // loop through the lines of result
        for (int currentLineIndex = 0; currentLineIndex < documentParsedInLines.length; ++currentLineIndex) {

            String lineOfResult = documentParsedInLines[currentLineIndex];

            // new line is delimiter of a new sentence
            if (lineOfResult.isEmpty()) {
                currentSentence = new Sentence(documentTree, currentParagraph);
                if (previousSentence != null) {
                    previousSentence.setNextSentence(currentSentence);
                    currentSentence.setPreviousSentence(previousSentence);
                }
                previousSentence = currentSentence;
                currentParagraph.getSentences().add(currentSentence);
            }

            // new word <f>, <d> for delimiter
            if (!lineOfResult.isEmpty()) {

                currentWord = parseWord(lineOfResult, currentSentence);

                if (previousWord != null) {
                    previousWord.setNextWord(currentWord);
                    currentWord.setPreviousWord(previousWord);
                }
                previousWord = currentWord;
                currentSentence.getWords().add(currentWord);
            }

        }
        return documentTree;
    }

    /**
     * Parses the word and extracts the information from a corresponding tag (line of the conll result)
     *
     * @param line     The line with starting <f> or <d> tag
     * @param sentence The parent sentence
     * @return the word class
     * @throws Exception general exception
     */
    private Word parseWord(final String line, final Sentence sentence) throws Exception {

        String[] linguisticParts = line.split("\t");

        if (linguisticParts.length < 8) {
            throw new Exception("Failed to create document tree at: " + line);
        }

        String word = linguisticParts[1].toLowerCase();

        String lemma = linguisticParts[2];

        int posAlternativeTag = lemma.indexOf("_^");
        if (posAlternativeTag > 0) {
            lemma = linguisticParts[2].substring(0, posAlternativeTag);
        }
        int posSigTag = lemma.indexOf("_,");
        if (posSigTag > 0) {
            lemma = lemma.substring(0, posSigTag);
        }
        int posExTag = lemma.indexOf("_;");
        if (posExTag > 0) {
            lemma = lemma.substring(0, posExTag);
        }
        int posExtraTag = lemma.indexOf("_:");
        if (posExtraTag > 0) {
            lemma = lemma.substring(0, posExtraTag);
        }
        if (lemma.endsWith("-1")) {
            lemma = StringUtils.reverse(lemma);
            lemma = lemma.replaceFirst("1-", "");
            lemma = StringUtils.reverse(lemma);
        }
        if (lemma.endsWith("-2")) {
            lemma = StringUtils.reverse(lemma);
            lemma = lemma.replaceFirst("2-", "");
            lemma = StringUtils.reverse(lemma);
        }
        if (lemma.endsWith("-3")) {
            lemma = StringUtils.reverse(lemma);
            lemma = lemma.replaceFirst("3-", "");
            lemma = StringUtils.reverse(lemma);
        }
        if (lemma.endsWith("-4")) {
            lemma = StringUtils.reverse(lemma);
            lemma = lemma.replaceFirst("4-", "");
            lemma = StringUtils.reverse(lemma);
        }
        lemma = lemma.toLowerCase();

        EWordType wordType;
        switch (linguisticParts[4].charAt(WORD_CLASS_POSITION)) {
            case 'N':
                wordType = EWordType.NOUN;
                break;
            case 'A':
                wordType = EWordType.ADJECTIVE;
                break;
            case 'P':
                wordType = EWordType.PRONOUN;
                break;
            case 'C':
                wordType = EWordType.NUMERAL;
                break;
            case 'V':
                wordType = EWordType.VERB;
                break;
            case 'D':
                wordType = EWordType.ADVERB;
                break;
            case 'R':
                wordType = EWordType.PREPOSITION;
                break;
            case 'T':
                wordType = EWordType.CONJUNCTION;
                break;
            case 'I':
                wordType = EWordType.INTERJECTIONS;
                break;
            case 'X':
                wordType = EWordType.UNKNOWN;
                break;
            case 'Z':
                wordType = EWordType.INTERPUNCTION;
                break;
            default:
                wordType = EWordType.UNKNOWN;
        } // end word class processing

        short parentNode = Short.parseShort(linguisticParts[6]);

        EConstituent constituent = parseConstituent(linguisticParts[7]);

        // TCH removes word negation, negation added into word lemma
        if (linguisticParts[4].charAt(NEGATION_POSITION_TAG) == 'N') {
            if (word.startsWith("ne")) {
                lemma = "ne" + lemma;
            }
        }
        boolean isLeft = Integer.parseInt(linguisticParts[0]) < parentNode;

        return new Word(new LinguisticWord(word, lemma, constituent, parentNode,
                new Morpho(wordType,
                        linguisticParts[4].charAt(GENUM_INDEX),
                        linguisticParts[4].charAt(NUMBER_INDEX),
                        linguisticParts[4].charAt(FALL_INDEX),
                        linguisticParts[4].charAt(PERSON_INDEX)), isLeft), sentence);
    }

    private EConstituent parseConstituent(String line) {
        switch (line) {
            case "Pred":
                return EConstituent.PREDICATE;
            case "Pnom":
                return EConstituent.PREDICATE_NOMINAL;
            case "Sb":
                return EConstituent.SUBJECT;
            case "Obj":
                return EConstituent.OBJECT;
            case "Adv":
                return EConstituent.ADVERBIALE;
            case "Atv":
                return EConstituent.ATTRIBUTE_VERBAL_NONVERB;
            case "AtvV":
                return EConstituent.ATTRIBUTE_VERBAL_VERB;
            case "Atr":
                return EConstituent.ATRIBUT;
            case "AuxV":
                return EConstituent.AUXILIARY_VERB;
            case "Coord":
                return EConstituent.COORD;
            case "AuxS":
                return EConstituent.ROOT;
            case "Apos":
                return EConstituent.APOS;
            case "ExD":
                return EConstituent.EX_DEPENDENT;
            default:
                return EConstituent.UNNECESSARY;
        }
    }

}
